package com.inspur.dgov.auth.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.inspur.dgov.bsp.user.feign.IUserFeign;
import com.inspur.dgov.common.boot.operation.RedissonObject;
import com.inspur.dgov.common.secure.constant.TokenConstant;
import com.inspur.dgov.common.secure.dto.AuthInfo;
import com.inspur.dgov.common.secure.dto.AuthUser;
import com.inspur.dgov.common.secure.dto.SecureUser;
import com.inspur.dgov.common.secure.utils.SecureUtil;
import com.inspur.dgov.common.tool.api.R;
import com.inspur.dgov.common.tool.constant.AppConstant;
import com.inspur.dgov.common.tool.utils.WebUtil;
import com.inspur.dgov.tenant.entity.PubTenant;
import com.inspur.dgov.tenant.feign.ITenantFeign;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 认证模块
 *
 * @Author chiyupei
 * @Date 19/6/27 下午4:10
 * @Version 1.0
 * @Description 认证模块
 */
@RestController
@Api(value = "用户授权认证", tags = "授权接口")
public class AuthController {

    @Autowired
    private RedissonObject redissonObject;

    @Autowired
    private IUserFeign userFeign;

    @Autowired
    private ITenantFeign tenantFeign;

    @Value("${tenant.default.code}")
    private String tenantCode;

    /**
     * 用户登录获取认证令牌
     * @param tenant
     * @param account
     * @param password
     * @param code
     * @param random
     * @return
     */
    @PostMapping("/token")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "tenant", value = "租户", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "account", value = "账号", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "password", value = "密码", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "code", value = "验证码", paramType = "query", dataType = "String"),
            @ApiImplicitParam(name = "random", value = "随机数", paramType = "query", dataType = "String")
    })
    @ApiOperation(value = "获取认证token", notes = "传入租户:tenant,账号:account,密码:password,验证码:code,随机数:random")
    public R<AuthInfo> token(@RequestParam(value="tenant", required = false) String tenant,
                             @RequestParam(value="account", required = true) String account,
                             @RequestParam(value="password", required = true) String password,
                             @RequestParam(value="code", required = true) String code,
                             @RequestParam(value="random", required = true) String random) {

        if (StrUtil.hasEmpty(account, password, code, random)) {
            return R.fail("接口调用不合法");
        }

        //图片验证码校验
        if (validImgcode(code, random)) {
            return R.fail("验证码不合法");
        }

        //验证客户端
        String[] tokens = SecureUtil.extractAndDecodeHeader();
        assert tokens.length == 2;
        String clientId = tokens[0];
        String clientSecret = tokens[1];
        //校验客户端信息
        if (!validateClient(clientId, clientSecret)) {
            return R.fail("客户端认证失败");
        }

        //获取登录租户信息
        if (StrUtil.isBlank(tenant)) {
            tenant = this.tenantCode;
        }
        R<PubTenant> tenantR = this.tenantFeign.getTenantInfoByCode(tenant);
        PubTenant pubTenant = tenantR.getData();
        if (null == pubTenant) {
            return R.fail("用户名或密码不正确");
        }

        //验证用户
        Long tenantId = pubTenant.getId();
        R<AuthUser> authUser = this.userLogin(account, password, tenantId);

        if (ObjectUtil.isNull(authUser)) {
            return R.fail("用户名或密码不正确");
        }

        Map userData = authUser.getData().getUser();

        //验证用户
        if (ObjectUtil.isNull(userData)) {
            return R.fail("用户名或密码不正确");
        }

        //设置jwt参数
        String userId = Convert.toStr(userData.get("ID"));
        String userName = null != userData.get("NAME") ? String.valueOf(userData.get("NAME")) : String.valueOf(userData.get("ACCOUNT"));
        String userType = authUser.getData().getUserType();

        Map<String, Object> param = new HashMap<>(16);
        param.put(TokenConstant.USER_ID, userId);
        param.put(TokenConstant.USER_NAME, userName);
        param.put(TokenConstant.USER_TYPE, userType);
        param.put(TokenConstant.TENANT_ID, tenantId);
        param.put(TokenConstant.ROLE_ID, CollUtil.join(authUser.getData().getRoles(), ","));
        param.put(TokenConstant.CLIENT_ID, clientId);

        //拼装accessToken
        String accessToken = SecureUtil.createJWT(param, "audience", "issuser", true);

        //缓存令牌、用户信息到redis中
        this.redisUser(accessToken, param);

        AuthInfo authInfo = new AuthInfo();
        authInfo.setUserId(userId);
        authInfo.setUserName(userName);
        authInfo.setUserType(userType);
        authInfo.setTenantId(Convert.toStr(tenantId));
        authInfo.setAuthority(CollUtil.join(authUser.getData().getRoles(), ","));
        authInfo.setAccessToken(accessToken);
        authInfo.setTokenType(TokenConstant.BEARER);
        //设置token过期时间
        authInfo.setExpiresIn(SecureUtil.getExpire());
        return R.data(authInfo);
    }

    /**
     * 刷新认证令牌
     * @param tenantCode
     * @return
     */
    @PostMapping("/refeshToken")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "tenantCode", value = "租户编码", paramType = "query", dataType = "String")
    })
    @ApiOperation(value = "刷新认证token", notes = "传入租户编码")
    public R<AuthInfo> refeshToken(@RequestParam(value="tenantCode", required = false) String tenantCode) {
        SecureUser user = SecureUtil.getUser();
        String tenantId = null;
        if (StrUtil.isEmpty(tenantCode)) {
            tenantId = user.getTenantId();
        } else {
            R<PubTenant> tenantR = this.tenantFeign.getTenantInfoByCode(tenantCode);
            PubTenant pubTenant = tenantR.getData();
            if (null == pubTenant) {
                tenantId = user.getTenantId();
            } else {
                tenantId = Convert.toStr(pubTenant.getId());
            }
        }
        Map<String, Object> param = new HashMap<>(16);
        param.put(TokenConstant.USER_ID, user.getUserId());
        param.put(TokenConstant.USER_NAME, user.getUserName());
        param.put(TokenConstant.USER_TYPE, user.getUserType());
        param.put(TokenConstant.TENANT_ID, tenantId);
        param.put(TokenConstant.ROLE_ID, user.getRoleId());
        param.put(TokenConstant.CLIENT_ID, user.getClientId());

        //拼装accessToken
        String accessToken = SecureUtil.createJWT(param, "audience", "issuser", true);

        AuthInfo authInfo = new AuthInfo();
        authInfo.setUserId(user.getUserId());
        authInfo.setUserName(user.getUserName());
        authInfo.setUserType(user.getUserType());
        authInfo.setTenantId(tenantId);
        authInfo.setAuthority(user.getRoleId());
        authInfo.setAccessToken(accessToken);
        authInfo.setTokenType(TokenConstant.BEARER);
        //设置token过期时间
        authInfo.setExpiresIn(SecureUtil.getExpire());
        return R.data(authInfo);
    }

    /**
     * 登出
     * @return
     */
    @PostMapping("/logout")
    @ApiOperation(value = "登出", notes = "", position = 2)
    public R logout() {
        HttpServletRequest request = WebUtil.getRequest();
        if (null != request) {
            String auth = request.getHeader(TokenConstant.HEADER);
            if ((auth != null) && (auth.length() > TokenConstant.AUTH_LENGTH)) {
                String headStr = auth.substring(0, 6).toLowerCase();
                if (headStr.compareTo(TokenConstant.BEARER) == 0) {
                    auth = auth.substring(7);
                    this.redissonObject.delete(auth);
                }
            }
        }
        return R.success("");
    }

    /**
     * 校验Client
     *
     * @param clientId     客户端id
     * @param clientSecret 客户端密钥
     * @return boolean
     */
    private boolean validateClient(String clientId, String clientSecret) {
//        R<BaseClient> res = this.tenantFeign.clientInfo(clientId);
//        BaseClient client = res.getData();
//        if (client != null) {
//            return StrUtil.equals(clientId, client.getClientId()) && StrUtil.equals(clientSecret, client.getClientSecret());
//        }
//        return false;
        return StrUtil.equals(clientId, "dgov") && StrUtil.equals(clientSecret, "dgov_secret");
    }

    /**
     * 图片验证码校验
     * @param imgCode
     * @param random
     * @return
     */
    private boolean validImgcode(String imgCode, String random) {
        boolean flag = false;
        String key = AppConstant.DEFAULT_CODE_KEY + random;
        Object codeObj = this.redissonObject.getValue(key);
        if (null == codeObj) {
            flag = true;
        } else {
            String saveCode = codeObj.toString();
            if (StrUtil.isBlank(saveCode)) {
                flag = true;
            } else if (!StrUtil.equalsIgnoreCase(saveCode, imgCode)) {
                flag = true;
            }
        }
        this.redissonObject.delete(key);
        return flag;
    }

    /**
     * 用户登录
     * @param account
     * @param password
     * @param tenantId
     * @return
     */
    private R<AuthUser> userLogin(String account, String password, Long tenantId) {
        return this.userFeign.userInfo(account, password, tenantId);
    }

    /**
     * 缓存令牌、用户信息到Redis中
     * @param accessToken
     * @param userMap
     */
    private void redisUser(String accessToken, Map<String, Object> userMap) {
        SecureUser user = new SecureUser();
        user.setClientId(StrUtil.toString(userMap.get("client_id")));
        user.setRoleId(StrUtil.toString(userMap.get("role_id")));
        user.setTenantId(StrUtil.toString(userMap.get("tenant_id")));
        user.setUserId(StrUtil.toString(userMap.get("user_id")));
        user.setUserName(StrUtil.toString(userMap.get("user_name")));
        user.setUserType(StrUtil.toString(userMap.get("user_type")));
        this.redissonObject.setValue(accessToken, user, SecureUtil.getExpire());
    }
}
